package csel.controller.factories;

import java.util.ArrayList;
import java.util.List;

import csel.controller.factories.AbstractActionListenerFactory;
import csel.controller.listeners.AreaViewActionListener;
import csel.controller.listeners.SplashViewActionListener;
import csel.controller.listeners.StatusViewActionListener;

import csel.controller.dispatchers.AbstractDispatcher;
import csel.controller.dispatchers.ModelDispatcher;
import csel.controller.dispatchers.ViewDispatcher;
import csel.controller.listeners.AbstractActionListener;
import csel.controller.listeners.KeyboardListener;
import csel.model.Model;
import csel.view.AreaViewport;
import csel.view.Splasher;
import csel.view.StatusViewport;

/**
 * Reasons for Existence: Constructs all relevant objects in the controller</br>
 * Team Members Responsible</br>
 * Implementation: Nik</br>
 * Testing: Nik</br> 
 * 
 * @author Nikolas Wolfe
 */
public class ControllerBuilder
{
	// Model Reference, Viewport References
	Model model;
	Splasher splashViewport;
	AreaViewport areaViewport;
	StatusViewport statusViewport;
	
	// Event Dispatchers
	AbstractDispatcher modelDispatcher;
	AbstractDispatcher viewDispatcher;
	
	// Listener Factories
	AbstractActionListenerFactory actionListenerFactory;
	KeyboardListenerFactory keyListenerFactory;
	
	// Command Factories
	ModelCommandFactory modelFactory;
	ViewCommandFactory viewFactory;
	
	// Concrete Listener Factories
	AbstractActionListenerFactory avalFactory = new AreaViewActionListenerFactory();
	AbstractActionListenerFactory spvalFactory = new SplashViewActionListenerFactory();
	AbstractActionListenerFactory stvalFactory = new StatusViewActionListenerFactory();
	KeyboardListenerFactory keyFactory = new KeyboardListenerFactory();
	
	// Instance Variables
	List<AbstractActionListener> aList;
	List<KeyboardListener> kList;
	
	// ...And Object said, "Let there be a Controller!"
	public ControllerBuilder(
			Model model, 
			Splasher splashViewport,
			AreaViewport areaViewport, 
			StatusViewport statusViewport)
	{
		this.model = model;
		this.splashViewport = splashViewport;
		this.areaViewport = areaViewport;
		this.statusViewport = statusViewport;
		
		modelFactory = new ModelCommandFactory( model, splashViewport, areaViewport, statusViewport );
		viewFactory = new ViewCommandFactory(model, splashViewport, areaViewport, statusViewport );
		
		modelDispatcher = new ModelDispatcher(model);
		viewDispatcher = new ViewDispatcher(splashViewport);
		System.out.println("[CONTROLLER] " + toString() + " initiated." +
			" Waiting for call to construct()...");
	}

	/*
	 * ------------------------
	 * *** PUBLIC INTERFACE *** 
	 * ------------------------
	 */
	public void contruct()
	{
		// do controller contruction
		System.out.println("[CONTROLLER] Controller construction initiated...");
		System.out.println("[CONTROLLER] Building Listeners... ");
		constructListeners(); // build listeners and start threads
		initiateDispatchers(); // initiate dispatcher threads
		
		// ... more operations ... 
		System.out.println("[CONTROLLER] Controller construction completed!");
	}
	
	public AbstractDispatcher getModelDispatcher()
	{
		return this.modelDispatcher;
	}

	public AbstractDispatcher getViewDispatcher()
	{
		return this.viewDispatcher;
	}

	public List<AbstractActionListener> getAList()
	{
		return aList;
	}

	public List<KeyboardListener> getKList()
	{
		return kList;
	}
	
	public String toString()
	{
	  	return this.getClass().getSimpleName();
	}
	
	/*
	 * -----------------
	 * PRIVATE INTERFACE
	 * -----------------
	 */
	// uses the instantiated factories to contstruct appropriate listener
	// objects and worker threads
	private void constructListeners()
	{
		// initiate the action listner set
		aList = new ArrayList<AbstractActionListener>();
		kList = new ArrayList<KeyboardListener>();
		
		// AreaViewActionListener
		useActionListenerFactory(avalFactory);
		
		// SplashViewActionListener
		useActionListenerFactory(spvalFactory);
		
		// StatusViewActionListener
		useActionListenerFactory(stvalFactory);
		
		// KeyboardListener
		useKeyboardListenerFactory(keyFactory);
		
		// ...potentially more listeners...
		System.out.println("[CONTROLLER] Listener contruction completed!");
	}
	
	private void useActionListenerFactory(AbstractActionListenerFactory factory)
	{
		setActionListenerFactory(factory);
		AbstractActionListener aal = constructActionListener();
		System.out.println("[CONTROLLER] Building " + aal.toString() + "... ");
		aList.add(aal);
		System.out.println("[CONTROLLER] Done!");
	}
	
	private void useKeyboardListenerFactory(KeyboardListenerFactory factory)
	{
		setKeyListenerFactory(factory);
		KeyboardListener kl = constructKeyListener();
		System.out.println("[CONTROLLER] Building " + kl.toString() + "... ");
		kList.add(kl);
		System.out.println("[CONTROLLER] Done!");
	}
	
	private AbstractActionListener constructActionListener()
	{
		AbstractActionListener q = actionListenerFactory.createAbstractActionListener();
		q.startWorker(); // start this listener's worker thread
		System.out.println("[CONTROLLER] " + q.toString()
				+ " created a worker thread.");
		return q;
	}

	private KeyboardListener constructKeyListener()
	{
		KeyboardListener q = keyListenerFactory.createKeyboardListner();
		q.startWorker(); // start this listener's worker thread
		System.out.println("[CONTROLLER] " + q.toString()
				+ " created a worker thread.");
		return q;
	}
	
	// setter for action listener factory
	private void setActionListenerFactory( AbstractActionListenerFactory aalf )
	{
		this.actionListenerFactory = aalf;
	}
	
	// setter for key listener factory
	private void setKeyListenerFactory( KeyboardListenerFactory klf )
	{
		this.keyListenerFactory = klf;
	}
	
	private void initiateDispatchers()
	{
		// launches dispatcher threads
		modelDispatcher.startWorker();
		System.out.println("[CONTROLLER] " + modelDispatcher.toString() + " created a worker thread.");
		viewDispatcher.startWorker();
		System.out.println("[CONTROLLER] " + viewDispatcher.toString() + " created a worker thread.");
	}
	
	/*
	 * -----------------
	 * PRIVATE FACTORIES
	 * -----------------
	 */
	// Returns new AreaViewActionListeners
	private class AreaViewActionListenerFactory extends AbstractActionListenerFactory
	{
		@Override // factory method
		public AbstractActionListener createAbstractActionListener()
		{
			return new AreaViewActionListener(modelDispatcher, viewDispatcher, modelFactory, viewFactory);
		}
		
		public String toString()
		{
			return getClass().getSimpleName();
		}
	}
	
	// Returns new SplashViewActionListeners
	private class SplashViewActionListenerFactory extends AbstractActionListenerFactory
	{
		@Override // factory method
		public AbstractActionListener createAbstractActionListener()
		{
			return new SplashViewActionListener(modelDispatcher, viewDispatcher, modelFactory, viewFactory);
		}
		
		public String toString()
		{
			return getClass().getSimpleName();
		}
	}
	
	// Returns new StatusViewActionListeners
	private class StatusViewActionListenerFactory extends AbstractActionListenerFactory
	{
		@Override // factory method
		public AbstractActionListener createAbstractActionListener()
		{
			return new StatusViewActionListener(modelDispatcher, viewDispatcher, modelFactory, viewFactory);
		}
		
		public String toString()
		{
			return getClass().getSimpleName();
		}
	}
	
	// Returns new KeyboardListeners
	public class KeyboardListenerFactory
	{
		// factory method
		public KeyboardListener createKeyboardListner()
		{
			return new KeyboardListener(modelDispatcher, viewDispatcher, modelFactory, viewFactory);
		}
		
		public String toString()
		{
			return getClass().getSimpleName();
		}
	}
}
